import React, {Component} from 'react';
import {Row, Col} from 'antd';
import {Toast, Modal} from 'antd-mobile';
import styles from './index.less';
import SocketEvent from '@/service/event';
import {error, log} from '@/utils/log-utils';
import {btnSound, playSound} from '@/utils/sounds-res';
import ChildTitleLayout from "@/header/child";
import {JOIN_ROOM_TYPE_FREEDOM, ROOM_STATUS_BATTLE} from "@/utils/const-utils";
import AdvancedBtn from "@/button";
import AdvancedSpin from "@/spinner";

/**
 * 匹配界面(开房模式)
 */
class PlayerFreedom extends Component {

  constructor(props) {
    super(props);
    this.state = {
      loading: false,
      text: null,
      // 用户要加入的房间号
      roomId: null,
      // 房间数据
      roomData: [],
      // 分页参数
      pageNum: 1,
      pageSize: 5,
      dataTotal: 1,
    };

    // 事件id
    this.joinRoomRespId = 'allJoinRoomRespId';
    this.leaveRoomRespId = 'allLeaveRoomRespId';
    this.syncRoomDataRespId = 'allSyncRoomDataRespId';
    this.matchSuccessRespId = 'allMatchSuccessRespId';
    this.roomStatusChangeRespId = 'allRoomStatusChangeRespId';
    this.allowInBattleId = 'allAllowInBattleId';
    this.kickUserId = 'allKickUserId';

    this.unRegisterServices = [];
    this.unRegisterServices.push(this.joinRoomRespId);
    this.unRegisterServices.push(this.leaveRoomRespId);
    this.unRegisterServices.push(this.syncRoomDataRespId);
    this.unRegisterServices.push(this.matchSuccessRespId);
    this.unRegisterServices.push(this.roomStatusChangeRespId);
    this.unRegisterServices.push(this.allowInBattleId);
    this.unRegisterServices.push(this.kickUserId);
  }

  componentDidMount() {
    const {userId} = this.props;
    const { pageSize } = this.state;
    log('进入匹配页面(开房模式) userId: ', userId);
    // 查询桌子列表存在延时，避免造成页面空缺，弄些空桌子
    const roomData = [];
    for(let i = 1; i <= pageSize; ++i) {
      roomData.push({ roomId: i, user: null, enemy: null });
    }
    this.setState({ roomData });

    // 查询列表数据
    this.queryDataList(true);
    // 开启事件监听
    this.startEventListen();
  }

  /**
   * 检查当前房间号是否在更新范围内
   * 如若不是用户能看到的房间，则不处理
   * @param roomId
   */
  inUserShowRoomIds = roomId => {
    const { pageNum, pageSize } = this.state;
    const begin = (pageNum - 1) * pageSize + 1;
    const end = pageNum * pageSize;
    return roomId >= begin && roomId <= end;
  }

  /**
   * 查询列表数据
   */
  queryDataList = (bindRoomLocation = false) => {
    const {userId} = this.props;
    const {pageNum, pageSize, roomId} = this.state;

    // 查询前检查用户是否在房间中，在房间则离开
    if (roomId) {
      this.leaveRoom(roomId);
      Toast.show('已离开房间');
    }

    log(`开始获取桌子数据列表, userId: ${userId} pageNum: ${pageNum} pageSize: ${pageSize}`);
    SocketEvent.emit(this.props.socket, 'roomListApi', {
      userId: userId,
      pageNum: pageNum,
      pageSize: pageSize,
      // 是否需要按用户所在房间的位置进行分页查询
      bindRoomLocation: bindRoomLocation,
    }, resp => {
      log(`桌子数据列表resp: `, resp);
      // this.setState({ loading: false });
      if (resp.code === 'success') {
        const {list, roomId, dataTotal} = resp.data;
        // 当用户在进入此页面就已经加入了某个房间时，通过房间号，计算当前应该到达的页数
        let newPageNum = pageNum;
        if (roomId) {
          newPageNum = Math.ceil(roomId / pageSize);
        }
        this.setState({roomData: list, roomId, dataTotal, pageNum: newPageNum});
      } else {
        Toast.show(resp.msg);
      }
    });
  };


  /**
   * 申请加入房间
   * @param roomId
   */
  applyJoinRoom = (roomId) => {
    playSound(btnSound);
    const {userId} = this.props;
    // 记录要加入的房间号
    this.setState({roomId});
    log(`开始服务器发送加入房间的请求:`, {userId: userId, roomId: roomId});
    SocketEvent.emit(this.props.socket, 'joinRoomApi', {
      userId: userId,
      joinType: JOIN_ROOM_TYPE_FREEDOM,
      appointRoomId: roomId,
    }, resp => {
      log('申请加入房间结果返回，resp: ', resp);
      if (resp.code !== 'success') {
        Toast.show(resp.msg);
        // 释放房间号
        this.setState({roomId: null });
      }
    });
  };

  /**
   * 事件监听
   */
  startEventListen = () => {
    // 同步房间信息结果
    SocketEvent.register('syncRoomDataRespApi', this.syncRoomDataRespId, resp => {
      log(`同步房间信息返回：`, resp);
      if (resp.code === 'success') {
        const {roomData} = this.state;
        const {roomUser, roomEnemy} = resp.data;
        const user = (roomUser || roomEnemy) || {};
        const roomId = user.roomId;
        log("同步房间信息，返回的roomId: ", roomId);

        const data = roomData.find(data => data.roomId === roomId);
        if (data) {
          const dataIndex = roomData.findIndex(data => data.roomId === roomId);
          data.user = roomUser;
          data.enemy = roomEnemy;
          // 替换值
          roomData.splice(dataIndex, 1, data);
          this.setState({roomData});
        } else {
          Toast.show('当页未加载到该房间信息');
        }
      } else {
        error('房间数据异常，错误信息：', resp.msg);
        // 房间已被解散
        Toast.fail(resp.msg, 2, () => {
          // 将当前人离开房间
          this.leaveRoom(this.state.roomId);
        });
      }
    });

    // 全局加入房间监听
    SocketEvent.register('joinRoomRespApi', this.joinRoomRespId, resp => {
      log('(全局)用户加入房间，resp：', resp);
      if (resp.code === 'success') {
        const {roomData} = this.state;
        const {roomId, userId} = resp.data;
        // 检查当前房间号是否能被用户看到
        if(!this.inUserShowRoomIds(roomId)) {
          log(`房间号不在用户可视范围内，roomId: [${roomId}]`)
          return;
        }

        const data = roomData.find(data => data.roomId === roomId);
        // 显示的数据包含该房间时同步房间信息，非用户本身不同步(用户自身加入房间时服务会主动推送房间信息)
        if (data && this.props.userId !== userId) {
          // 主动同步房间数据
          log(`主动同步房间的数据，roomId: [${roomId}], userId: [${userId}]`);
          SocketEvent.emit(this.props.socket, 'syncRoomDataApi', {
            userId: this.props.userId,
            roomId: roomId,
          }, resp => {
            if (resp.code !== 'success') {
              Toast.show(resp.msg);
            }
          });
        }
      }
    });

    // 全局离开房间事件
    SocketEvent.register('leaveRoomRespApi', this.leaveRoomRespId, resp => {
      log('(全局)离开了房间，返回：', resp);
      if (resp.code === 'success') {
        const {roomData} = this.state;
        const {roomId, userId} = resp.data;
        // 检查当前房间号是否能被用户看到
        if(!this.inUserShowRoomIds(roomId)) {
          log(`房间号不在用户可视范围内，roomId: [${roomId}]`)
          return;
        }

        const data = roomData.find(data => data.roomId === roomId);
        // 只处理在本页显示的数据
        if (data) {
          log('用户离开的逻辑处理：', roomData);
          const dataIndex = roomData.findIndex(data => data.roomId === roomId);
          if (data.user?.userId === userId) {
            data.user = null;
          } else if (data.enemy?.userId === userId) {
            data.enemy = null;
          }
          roomData.splice(dataIndex, 1, data);
          this.setState({roomData});
        }
      }
    });

    // 全局房间状态改变通知
    SocketEvent.register('roomStatusChangeRespApi', this.roomStatusChangeRespId, resp => {
      log('(全局)房间状态改变，返回：', resp);
      if (resp.code === 'success') {
        const { userId } = this.props;
        const { roomId } = resp.data;
        // 检查当前房间号是否能被用户看到
        if(!this.inUserShowRoomIds(roomId)) {
          log(`房间号不在用户可视范围内，roomId: [${roomId}]`)
          return;
        }

        // 通知后主动请求房间的数据
        SocketEvent.emit(this.props.socket, 'syncRoomDataApi', {
          userId: userId,
          roomId: roomId,
        }, resp => {
          if (resp.code !== 'success') {
            Toast.show(resp.msg);
          }
        });
      }
    });

    // 双方已准备，但还未保存数据
    SocketEvent.register('matchSuccessRespApi', this.matchSuccessRespId, (resp) => {
      log('服务器返回双方已准备信息，resp: ', resp);
      if (resp.code === 'success') {
        this.setState({loading: true, text: '玩命加载中...'});
      } else {
        Toast.show(resp.msg);
      }
    });

    // 允许进入对局
    SocketEvent.register('allowInBattleApi', this.allowInBattleId, (resp) => {
      log('服务器允许双方进入对局，resp: ', resp);
      if (resp.code === 'success') {
        const {userId} = this.props;
        const {roomId} = this.state;
        const {battleId, playOne, playTwo} = resp.data;
        const roomUser = playOne.userId === userId ? playOne : playTwo;
        const roomEnemy = playOne.userId === userId ? playTwo : playOne;
        log(`即将进入对战页面, userId: ${roomUser.userId}, roomId: ${roomId}`);
        this.props.matchSuccess(battleId, roomId, roomUser, roomEnemy);
      } else {
        Toast.show(resp.msg);
      }
    });

    /**
     * 被踢了房间
     */
    SocketEvent.register('kickUserRespApi', this.kickUserId, (resp) => {
      log('被房主踢出房间，resp: ', resp);
      if (resp.code === 'success') {
        const { userId, roomId } = resp.data;
        // 被踢出房间时，全局列表会通知用户离开，这里只做提醒
        if(this.props.userId === userId && this.state.roomId === roomId) {
          this.setState({ roomId: null });
          Modal.alert('系统提示', '您被房主请出了房间', [
            {
              text: '确认', onPress: () => {
                playSound(btnSound);
              },
            },
          ]);
        }
      } else {
        Toast.show(resp.msg);
      }
    });
  };


  /**
   * 用户准备
   * @param roomId
   */
  userReady = (roomId) => {
    playSound(btnSound);
    const {userId} = this.props;
    this.setState({roomId});
    log('点击『开始』按钮，roomId:', roomId);
    // 通知服务器，玩家已准备
    SocketEvent.emit(this.props.socket, 'userReadyApi', {
      userId: userId,
      roomId: roomId,
    }, resp => {
      log('用户已准备，开始同步房间信息,roomId:', this.state.roomId, resp);
      // 通知后主动请求房间的数据
      SocketEvent.emit(this.props.socket, 'syncRoomDataApi', {
        userId: userId,
        roomId: roomId,
      }, resp => {
        if (resp.code !== 'success') {
          Toast.show(resp.msg);
        }
      });
    });
  }

  /**
   * 离开房间
   * @param roomId
   */
  leaveRoom = (roomId) => {
    playSound(btnSound);
    log('离开房间，roomId：', roomId);
    const {userId} = this.props;
    // 通知服务器将该用户从房间中踢出
    SocketEvent.emit(this.props.socket, 'leaveRoomApi', {
      userId: userId,
      roomId: roomId,
    }, resp => {
      log('离开房间结果返回: ', resp);
      if (resp.code === 'success') {
        this.setState({roomId: null});
      } else {
        Toast.show(resp.msg);
        log('无法离开该房间', roomId);
      }
    })
  }


  /**
   * 返回上一页
   * @param e
   */
  goBack = e => {
    playSound(btnSound);
    const {roomId} = this.state;
    // 若用户加入了房间则先离开房间
    if (roomId) {
      // 通知服务器将该用户从房间中踢出
      log(`用户[${this.props.userId}]即将退出房间[${roomId}]`);
      SocketEvent.emit(this.props.socket, 'leaveRoomApi', {
        userId: this.props.userId,
        roomId: roomId,
      }, resp => {
        this.setState({loading: false});
        log('离开房间结果返回: ', resp);
        if (resp.code === 'success') {
          this.props.goBack();
        } else {
          Toast.show(resp.msg);
        }
      });
    } else {
      // 用户未进入房间直接退出
      this.props.goBack();
    }
  };

  /**
   * 组件销毁时将所有已注册的监听卸载掉
   */
  componentWillUnmount() {
    SocketEvent.unRegister(this.unRegisterServices);
  }

  render() {
    const {userId} = this.props;
    const {roomId} = this.state;

    const {roomData, pageNum, pageSize, dataTotal} = this.state;
    return (
      <div className={styles.wrap}>
        <ChildTitleLayout text={'选择房间'}/>
        <div className={styles.content}>
          {
            roomData.map(data => {
              const { user: roomUser, enemy: roomEnemy } = data;
              return (
                <div key={data.roomId} className={styles.desk}>
                  <div className={styles.container}>
                    <div className={styles.roomId}><span>{data.roomId}</span></div>
                    <div hidden={!(roomUser?.roomStatus === ROOM_STATUS_BATTLE)} className={styles.state}>
                      已开局
                    </div>
                    <div
                      className={styles.joinRoom}
                      hidden={(roomUser && roomEnemy) || !!roomId}
                      onClick={e => this.applyJoinRoom(data.roomId)}
                    >
                      进入房间
                    </div>
                    <div className={styles.user}>
                      <div className={styles.icon} hidden={!roomUser}>
                        <img src={roomUser?.iconUrl || require('@/assets/images/user.png')}
                             width='100%'
                             height='100%'
                             alt='user'
                        />
                        <div>{roomUser?.userName}</div>
                      </div>
                      <div className={styles.detail} hidden={!roomUser}>
                        <div className={styles.userId}>
                          <div><span>积分：</span><span>{roomUser?.score}</span></div>
                        </div>
                        <div className={styles.score}>
                          <div>
                            <span>胜率：</span>
                            <span>
                              {roomUser && roomUser.pkTotalCount > 0
                                ? Math.floor((roomUser.pkWinCount / roomUser.pkTotalCount) * 100) : 0}%
                            </span>
                          </div>
                        </div>
                        <div className={styles.ready}>
                          {/*未准备时，此操作隐藏*/}
                          <div
                            hidden={!roomUser?.isReady || roomUser?.roomStatus === ROOM_STATUS_BATTLE}
                            className={styles.status}>
                            <span>{roomUser?.isReady ? '准备' : ''}</span>
                          </div>
                          <div
                            hidden={roomUser?.isReady || roomUser?.userId !== userId}
                            className={styles.start}
                            style={{marginRight: '5px'}}
                            onClick={e => this.userReady(data.roomId)}>
                            开始
                          </div>
                          <div
                            hidden={!roomUser || roomUser?.userId !== userId}
                            className={styles.start}
                            onClick={e => this.leaveRoom(data.roomId)}>
                            离开
                          </div>
                        </div>
                      </div>
                    </div>
                    <div className={styles.enemy}>
                      <div className={styles.detail} hidden={!roomEnemy}>
                        <div className={styles.userId}>
                          <div><span>积分：</span><span>{roomEnemy?.score}</span></div>
                        </div>
                        <div className={styles.score}>
                          <div>
                            <span>胜率：</span>
                            <span>
                              {roomEnemy && roomEnemy.pkTotalCount > 0
                                ? Math.floor((roomEnemy.pkWinCount / roomEnemy.pkTotalCount) * 100) : 0}%
                            </span>
                          </div>
                        </div>
                        <div className={styles.ready}>
                          <div
                            hidden={!roomEnemy || roomEnemy?.userId !== userId}
                            className={styles.start}
                            onClick={e => this.leaveRoom(data.roomId)}>
                            离开
                          </div>
                          <div
                            hidden={roomEnemy?.isReady || roomEnemy?.userId !== userId}
                            className={styles.start}
                            style={{marginLeft: '5px'}}
                            onClick={e => this.userReady(data.roomId)}>开始
                          </div>
                          <div
                            hidden={!roomEnemy?.isReady || roomEnemy?.roomStatus === ROOM_STATUS_BATTLE}
                            className={styles.status}>
                            <span>{roomEnemy?.isReady ? '准备' : ''}</span>
                          </div>
                        </div>
                      </div>
                      <div className={styles.icon} hidden={!roomEnemy}>
                        <img src={roomEnemy?.iconUrl || require('@/assets/images/user.png')}
                             width='100%'
                             height='100%'
                             alt='user'
                        />
                        <div>{roomEnemy?.userName}</div>
                      </div>
                    </div>
                  </div>
                </div>
              )
            })
          }
          <div className={styles.floor}>
            <Row>
              <Col span={7}>
                <AdvancedBtn
                  type="normal"
                  text="上页"
                  disabled={pageNum === 1}
                  onClick={async e => {
                    playSound(btnSound);
                    if (pageNum === 1) {
                      Toast.show('已达第一页')
                    } else {
                      await this.setState({pageNum: pageNum - 1});
                      this.queryDataList();
                    }
                  }}
                />
              </Col>
              <Col span={1}/>
              <Col span={7}>
                <AdvancedBtn
                  type="normal"
                  text="下页"
                  disabled={(pageNum * pageSize) >= dataTotal}
                  onClick={async e => {
                    playSound(btnSound);
                    if ((pageNum * pageSize) >= dataTotal) {
                      Toast.show('已达最后一页')
                    } else {
                      await this.setState({pageNum: pageNum + 1});
                      this.queryDataList();
                    }
                  }}
                />
              </Col>
              <Col span={1}/>
              <Col span={8}>
                <AdvancedBtn type="danger" text="返回" onClick={e => this.goBack(e)} />
              </Col>
            </Row>
          </div>
        </div>
        <AdvancedSpin text={this.state.text} show={this.state.loading}/>
      </div>
    );
  }
}

export default PlayerFreedom;
